/**
 * 项目相关中间件
 *
 * Created by snowingsea on 2020/02/12.
 */
const mongoose = require('mongoose');
const path = require('path');
const projects = require('./../../models/projects');
const projectVersions = require('./../../models/project-versions');
const projectVersionHistories = require('./../../models/project-version-histories');
const PhoenixError = require('./../../models/phoenix-error');
const utility = require('./../../models/utility');
const analyzeTemplate = require('./../../models/analyze-templates');
const CodeInstrum = require('../../models/mongo/code-instrumentation');
const cacheUtil = require('../../cacheUtil');

/**
 * 判断URL参数中项目版本Id是否存在
 * @param req
 * @param res
 * @param next
 */
function requireProjectVersionExist(req, res, next) {
  const { projectVersionId } = req.params;
  if (!projectVersionId) {
    next(new PhoenixError('invalid request', 'Missing parameters. "projectVersionId" is required'));
    return;
  }
  projectVersions.getVersion(projectVersionId)
    .then((projectVersion) => {
      if (!projectVersion) {
        return Promise.reject(new PhoenixError('not found', 'Not found, projectVersion not found'));
      }
      req.$projectVersion = projectVersion;
      return projects.getProject(projectVersion.projectId);
    })
    .then((project) => {
      if (!project) {
        return Promise.reject(new PhoenixError('not found', 'Not found, project not found'));
      }
      req.$project = project;
      return Promise.resolve();
    })
    .then(() => {
      next();
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

function requireCodeInstrumentationExist(req, res, next) {
  (async () => {
    const { codeInstrumentationId, projectVersionId } = req.params;
    const codeInstrum = await CodeInstrum.findById(mongoose.Types.ObjectId(codeInstrumentationId));
    if (!codeInstrum) {
      throw new PhoenixError('not found', 'Not found, codeInstrumentation not found');
    }
    req.$codeInstrumentation = codeInstrum;
    req.$codeInstrumentationDir = path.join(cacheUtil.getCodeInstrumentationDir(), projectVersionId.toString(), codeInstrumentationId.toString());
  })()
    .then(() => next())
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 要求当前项目版本的可编辑
 * @param req
 * @param res
 * @param next
 */
function requireProjectVersionAvailable(req, res, next) {
  let err;
  switch (req.$projectVersion.status) {
    case projectVersions.statusKind.frozen:
      err = new PhoenixError('forbidden', 'The project version is frozen');
      break;
    case projectVersions.statusKind.deleting:
      err = new PhoenixError('forbidden', 'The project version is deleting');
      break;
    default:
      break;
  }
  next(err);
}

/**
 * 解析项目版本请求参数
 * @param req
 * @param res
 * @param next
 */
function parseVersionQuery(req, res, next) {
  req.query.includes = utility.parseArray(req.query, 'includes');
  req.body.includes = utility.parseArray(req.body, 'includes');
  if (!req.query.includes) {
    req.query.includes = req.body.includes;
  } else if (req.body.includes) {
    req.query.includes = req.query.includes.concat(req.body.includes);
  }
  if (req.query.macros) {
    try {
      req.query.macros = JSON.parse(req.query.macros);
    } catch (e) {
      next(new PhoenixError('invalid request', 'macros should be a JSON string'));
      return;
    }
  }
  if (req.body.macros) {
    try {
      req.body.macros = JSON.parse(req.body.macros);
    } catch (e) {
      next(new PhoenixError('invalid request', 'macros should be a JSON string'));
      return;
    }
  }
  req.query.macros = utility.parseArray(req.query, 'macros');
  req.body.macros = utility.parseArray(req.body, 'macros');
  if (!req.query.macros) {
    req.query.macros = req.body.macros;
  } else if (req.body.macros) {
    req.query.macros = req.query.macros.concat(req.body.macros);
  }
  if (req.query.macros) {
    req.query.macros = req.query.macros.filter(({ key }) => key);
  }
  [
    'caseSensitive',
    'strictOutputCheck',
    'ignoreAsm',
    'environmentChange',
    'integrateStdLib',
    'integrateQtLib',
  ].forEach((key) => {
    switch (req.query[key]) {
      case 'true':
        req.query[key] = true;
        break;
      case 'false':
        req.query[key] = false;
        break;
      default:
        break;
    }
  });
  next();
}

/**
 * 判断项目版本历史是否存在
 * @param req
 * @param res
 * @param next
 */
function requireProjectVersionHistoryExist(req, res, next) {
  if (!req.params.projectVersionHistoryId) {
    next(new PhoenixError('invalid request', 'Missing parameters. "projectVersionHistoryId" is required'));
    return;
  }

  projectVersionHistories.getHistory(req.params.projectVersionHistoryId)
    .then((projectVersionHistory) => {
      if (!projectVersionHistory) {
        return Promise.reject(new PhoenixError('not found', 'Not found, project version history not found'));
      }
      req.$projectVersionHistory = projectVersionHistory;
      return projectVersions.getVersion(projectVersionHistory.versionId);
    })
    .then((projectVersion) => {
      if (!projectVersion) {
        return Promise.reject(new PhoenixError('not found', 'Not found, project version not found'));
      }
      req.$projectVersion = projectVersion;
      return projects.getProject(projectVersion.projectId);
    })
    .then((project) => {
      if (!project) {
        return Promise.reject(new PhoenixError('not found', 'Not found, project not found'));
      }
      req.$project = project;
      return Promise.resolve();
    })
    .then(() => {
      next();
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 判断检测模板是否存在
 * @param req
 * @param res
 * @param next
 */
function requireAnalyzeTemplateExist(req, res, next) {
  if (!req.$projectVersion.analyzeTemplate) {
    next(new PhoenixError('not found', 'Analyze template not found'));
    return;
  }
  analyzeTemplate.getTemplate(req.$projectVersion.analyzeTemplate)
    .then((projectVersion) => {
      if (!projectVersion) {
        return Promise.reject(new PhoenixError('not found', 'Analyze template not found'));
      }
      return Promise.resolve();
    })
    .then(() => {
      next();
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

function requireRegressionVersionExist(req, res, next) {
  let sourceVersion;
  if (!req.$projectVersion.oldVersionId) {
    sourceVersion = Promise.resolve(null);
  } else {
    sourceVersion = projectVersions.getVersion(req.$projectVersion.oldVersionId);
  }
  sourceVersion
    .then((version) => {
      if (!version) {
        return Promise.reject(new PhoenixError('precondition failed', 'Source version not found'));
      }
      return Promise.resolve();
    })
    .then(() => {
      next();
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * Export model definition object.
 */
module.exports = {
  requireAnalyzeTemplateExist,
  requireProjectVersionExist,
  requireProjectVersionAvailable,

  parseVersionQuery,

  requireProjectVersionHistoryExist,
  requireRegressionVersionExist,
  requireCodeInstrumentationExist,
};
